/*
 *	MIDAS Context Based Routing
 *	scheduler.cc
 *	Copyright (c) 2007 Bartlomiej Zolnierkiewicz
 *	MEAG IT PW
 *
 *	Loosely based on NS2 scheduler implementation.
 */

#include <stdio.h>
#include <stdlib.h>
#include <limits.h>
#include <math.h>
#include <sys/time.h>

#include "scheduler.h"
#include "packet.h"

Scheduler* Scheduler::instance_;
scheduler_uid_t Scheduler::uid_ = 1;

/**
 * Scheduler class destructor.
 */

Scheduler::~Scheduler(){
	instance_ = NULL ;
}

/**
 * Gets current clock.
 *
 * Used by events scheduler.
 *
 * @returns the current time presented as miliseconds since epoch.
 */

unsigned long sclock()
{
	struct timeval tv;
	unsigned long now = 0;

	gettimeofday(&tv, NULL);

	now = 1000ULL * tv.tv_sec;
	now += ((unsigned long)tv.tv_usec) / 1000;

	return now;
}

/**
 * Schedules an event.
 *
 * @param h event handler
 * @param e event
 * @param delay event delay
 *
 * Schedules the event @a e to be executed using handler @a h after
 * minimum delay of @a delay miliseconds.
 */

void
Scheduler::schedule(Handler *h, Event* e, double delay)
{
//	cbr_debug("Scheduler: Event UID(%d) UID_(%d)\n", e->uid_, uid_);

	if (!h) {
		cbr_log("Scheduler: attempt to schedule an event with a NULL handler.");
		abort();
	};

	if (e->uid_ > 0) {
		cbr_log("Scheduler: Event UID(%d) not valid!\n\n", e->uid_);
		abort();
	}

	if (delay < 0) {
		cbr_log("Scheduler: scheduling event with negative delay (%f) "
			"at time %lu.\n", delay, sclock());
	}

	if (uid_ < 0) {
		cbr_log("Scheduler: UID space exhausted!\n");
		abort();
	}

	e->uid_ = uid_++;
	e->handler_ = h;

	unsigned long t = sclock() + (unsigned long)(delay * 1000);
	e->time_ = t;

	insert(e);
}

/**
 * Runs events scheduler.
 */

void
ListScheduler::run()
{
	instance_ = this;
	Event** p = &queue_;
	unsigned long now = sclock();

	while (*p) {
		Event *e = *p;

		if (e->time_ < now) {
			// dequeue
			*p = e->next_;
			dispatch(e, e->time_);
		} else {
			// next element in the list
			p = &(*p)->next_;
		}
	}
}

/**
 * Dispatches one event from the events list of the scheduler.
 *
 * @param p event
 * @param t time (unused currently)
 */

void
Scheduler::dispatch(Event* p, unsigned long t)
{
//	cbr_debug("in dispatch UID(%d)\n", p->uid_);

	p->uid_ = -p->uid_;	// mark as being dispatched
	p->handler_->handle(p);	// dispatch
}

/**
 * Inserts one event into events list of the scheduler.
 *
 * @param e event
 */

void
ListScheduler::insert(Event* e)
{
	unsigned long t = e->time_;
	Event** p;

	for (p = &queue_; *p != 0; p = &(*p)->next_)
		if (t < (*p)->time_)
			break;

	e->next_ = *p;
	*p = e;
}
